package com.chaihu.jpaDemo.common.aspect;

import com.alibaba.fastjson.JSON;
import com.chaihu.jpaDemo.common.thread.log.OperateLogThread;
import com.chaihu.jpaDemo.modules.common.dto.ResultObject;
import com.chaihu.jpaDemo.modules.system.entity.OperateLog;
import com.chaihu.jpaDemo.modules.system.service.impl.OperateLogServiceImpl;
import com.chaihu.jpaDemo.utils.SpringUtil;
import com.chaihu.jpaDemo.utils.ThreadLocalUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;

/**
 * 描述: 方法执行时长统计
 * 作者: qinzhw
 * 创建时间: 2018/7/11 14:34
 */
@Aspect
@Component
public class LogAspect {

    private final static Logger logger = LoggerFactory.getLogger(LogAspect.class);


    @Autowired
    public ExecutorService executorService;


    @Pointcut("execution(public * com.chaihu.jpaDemo.modules.*.controller..*(..))")
	public void log(){
	}


    @Before("log()")
    public void doBefore(JoinPoint joinPoint) {
        ThreadLocalUtils.getTimeTreadLocal().set(System.currentTimeMillis());

        // 接收到请求，记录请求内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();


        String token = request.getHeader("token");
        if(StringUtils.isBlank(token)){
            token = request.getParameter("token");
            if (StringUtils.isBlank(token)) {
                token = request.getSession().getId();
            }
        }

        // 记录下请求内容
        OperateLog operateLog = new OperateLog();
        operateLog.setOperateName(joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
        operateLog.setOperateDate(new Date());
        operateLog.setUrl(request.getRequestURL().toString());
        operateLog.setMethod(request.getMethod());
        String ip = request.getHeader("X-Real-IP");
        operateLog.setIp(ip==null? request.getRemoteAddr():ip);
        Map<String, String[]> parameter = new HashMap<String, String[]>(request.getParameterMap());
        if (parameter.containsKey("password")) {
            parameter.put("password",null);
        }
        StringBuffer inputJson = new StringBuffer(JSON.toJSONString(parameter));
        logger.info("token:{},url:{},input:{}",token,operateLog.getUrl(),inputJson);
        operateLog.setParams(inputJson.toString());
        /*if (user!=null) {
            operateLog.setUserId(user.getId());
            operateLog.setUserName( user.getAccount());
        } else {*/
            operateLog.setUserId(-1L);
            operateLog.setUserName("访客");

        ThreadLocalUtils.getOperateLogThreadLocal().set(operateLog);
    }

	@AfterReturning(returning = "obj",pointcut = "log()")
    public void doAfterReturing(Object obj) throws IllegalArgumentException{
        OperateLog operateLog = ThreadLocalUtils.getOperateLogThreadLocal().get();
        long lastTime = System.currentTimeMillis() - ThreadLocalUtils.getTimeTreadLocal().get();

        StringBuffer buf = new StringBuffer("执行:(")
                .append(operateLog.getUrl()).append(") BEGIN=")
                .append(ThreadLocalUtils.getTimeTreadLocal().get()).append(", END=")
                .append(System.currentTimeMillis()).append(", 耗时=").append(lastTime);
        logger.info(buf.toString());
        try {
            // 记录下请求内容
            operateLog.setTime(lastTime);
            operateLog.setType(0);
            if (obj instanceof ResultObject) {
                StringBuilder logJson = new StringBuilder(JSON.toJSONString(obj));
                operateLog.setOperateResult(logJson.length()>10000?"return too long!":logJson.toString());
            }
            logger.info("operateLog：{}",operateLog.toString());

            //数据量太大暂时不保存数据库
            operateLog.setOperateResult(null);
            executorService.submit(new OperateLogThread(operateLog));
            /*saveLog(operateLog);*/
        } catch (Exception e1){
            logger.error("try-catch："+e1.toString());
        } finally {
            ThreadLocalUtils.getTimeTreadLocal().remove();
            ThreadLocalUtils.getOperateLogThreadLocal().remove();
        }
    }

    @AfterThrowing(pointcut = "log()", throwing = "e")
    public void doAfterThrowing(JoinPoint point, Throwable e) {
        OperateLog operateLog = ThreadLocalUtils.getOperateLogThreadLocal().get();
        if (operateLog != null) {
            operateLog.setType(1);
            operateLog.setException(e.toString());
            operateLog.setTime(System.currentTimeMillis() - ThreadLocalUtils.getTimeTreadLocal().get());
            logger.error("operateLog :{}", operateLog.toString());
            try {
                executorService.submit(new OperateLogThread(operateLog));
            } catch (Exception e1){
                logger.error(e1.toString());
            } finally {
                ThreadLocalUtils.getTimeTreadLocal().remove();
                ThreadLocalUtils.getOperateLogThreadLocal().remove();
            }
        }
    }

//    @Around(value = "log()")
//    public Object doAroundAdvice(ProceedingJoinPoint proceedingJoinPoint){
//        logger.info("DictTranslationAspect 环绕通知的目标方法名："+proceedingJoinPoint.getSignature().getName());
//        try {
//            Object obj = proceedingJoinPoint.proceed();
//            if (obj instanceof ResultObject) {
//                Object data = ((ResultObject) obj).getData();
//            }
//            return obj;
//        } catch (Throwable throwable) {
//            throwable.printStackTrace();
//            return ResultObject.error(throwable.getMessage());
//        }
//    }

    @Async("AsyncTaskExecutor")
    public void saveLog(OperateLog operateLog){
        OperateLogServiceImpl logService = SpringUtil.getBean(OperateLogServiceImpl.class);
        logService.save(operateLog);
    }

}
